"""
文件下载管理模块 - 负责TF卡文件操作和数据下载
"""
import os
import csv
import re
import struct
from typing import List, Optional, Set
from core.event_bus import event_bus, EventType
from core.config import Config

class DownloadManager:
    """下载管理器"""
    
    def __init__(self, serial_manager):
        self.serial_manager = serial_manager
        self.download_path = Config.get_default_download_path()
        
        # 文件下载状态
        self.is_downloading = False
        self.current_file = None
        self.file_buffer = bytearray()
        self.recv_buffer = bytearray()
        self.content_list = []
        self.expected_lines = 0
        self.received_lines = 0
        self.received_seq_set: Set[int] = set()
        
        # 文件列表
        self.files_list = []
        # 新增：区分“列表阶段”和“下载阶段”的独立标志，避免互相阻塞
        self.is_listing = False
        
        # 订阅事件
        event_bus.subscribe(EventType.SERIAL_DATA_RECEIVED, self._handle_download_data)
    
    def set_download_path(self, path: str):
        """设置下载路径"""
        self.download_path = path
    
    def get_file_list(self) -> bool:
        """获取设备文件列表"""
        if not (self.serial_manager.is_connected and self.serial_manager.is_receiving):
            return False
            
        self.files_list.clear()
        self.file_buffer.clear()

         # 进入“获取列表阶段”
        self.is_listing = True
        self.is_downloading = False
        self.current_file = None
        
        success = self.serial_manager.send_data(Config.CMD_TYPES['GET_FILES'])
        return success
    
    def download_file(self, filename: str) -> bool:
        """下载指定文件"""
        if not (self.serial_manager.is_connected and self.serial_manager.is_receiving):
            print("[download] 串口未打开或未开始接收，拒绝新请求")
            return False
        if self.is_downloading:
            print("[download] 已有文件正在下载，拒绝新请求")
            return False
        
        if self.current_file is not None:
            # 已有文件正在下载，拒绝并返回
            print("[download] 已有文件正在下载，拒绝新请求")
            return False
            
        self.current_file = filename
        self.content_list.clear()
        self.expected_lines = 0
        self.received_lines = 0
        self.received_seq_set.clear()
        self.recv_buffer.clear()
        # 退出“列表阶段”，进入“下载阶段”
        self.is_listing = False

        success = self.serial_manager.send_data(
            Config.CMD_TYPES['SET_INTERVAL'],
            Config.CTRL_MSG_TYPES['CONTROL_INPUT'],
            filename,
            control_id='0013'
        )
        
        if success:
            self.is_downloading = True
        else:
            # 发送失败，回滚状态
            self.current_file = None
            
        return success
    
    def delete_file(self, filename: str) -> bool:
        """删除设备文件"""
        
        if not self.serial_manager.is_connected:
            return False
            
        return self.serial_manager.send_data(
            Config.CMD_TYPES['SET_INTERVAL'],
            Config.CTRL_MSG_TYPES['CONTROL_INPUT'],
            filename,
            control_id='0014'
        )
    
    def _handle_download_data(self, data):
        """处理下载数据"""
        # 仅处理字节流；其他字符串帧给 DataProcessor 去用
        if not (self.is_listing or self.is_downloading):
            return
        #严格过滤非字节流，避免在 bytearray.extend 时抛出“expected iterable of integers; got: 'str'”
        if not isinstance(data, (bytes, bytearray)):
            return
            
        if self.current_file is None:
            # 接收文件列表
            if self.is_listing:
                # print(f"[download] 接收文件列表数据: {data}")
                self._process_file_list(data)
            else:
                # 未在列表阶段也未处于下载阶段的字节，忽略
                return
        else:
            # 接收文件内容
            if self.is_downloading:
                self._process_file_data(data)
            else:
            # 未标记下载中，但 current_file 不为空，保护性忽略
                return
            
    
    def _process_file_list(self, data: bytes):
        """处理文件列表数据"""
        self.file_buffer.extend(data)    # b'数据缓存
        self._file_list_from_stream(self.file_buffer,self.files_list) # 转换为列表字符串
        # print(f"[download] 解析文件列表: {self.files_list}")
        
        if self.files_list:
            # event_bus.emit(EventType.FILE_LIST_RECEIVED, self.files_list.copy())
            event_bus.emit(EventType.FILE_LIST_RECEIVED, self.files_list)
    
     # 解析数据文件
    def _file_list_from_stream(self, buffer, file_list):
        data = bytes(buffer)
        last_pos = 0
        while True:
            idx = data.find(b'.csv', last_pos)
            if idx == -1:
                break

            # 回溯允许字符（数字、-、_）
            name_start = idx
            while name_start > 0:
                b = data[name_start - 1]
                if (48 <= b <= 57) or b == 45 or b == 95:  # 0-9, '-', '_'
                    name_start -= 1
                else:
                    break

            filename_bytes = data[name_start:idx + 4]

            # 去除可能的前导 '-' 或 '_'，推进到第一个数字
            while filename_bytes and not (48 <= filename_bytes[0] <= 57):
                filename_bytes = filename_bytes[1:]

            # .csv 后跟 4 字节大端文件大小
            size_start = idx + 4
            size_end = size_start + 4
            if len(data) < size_end:
                # 数据未收全：保留从文件名开始的尾部，等待下次补齐
                last_pos = name_start
                break

            size_val = int.from_bytes(data[size_start:size_end], byteorder='big', signed=False)

            # 解码文件名并校验格式
            try:
                filename = filename_bytes.decode('ascii', errors='ignore')
            except Exception:
                filename = filename_bytes.decode(errors='ignore')

            # 严格匹配 YYYY-MM-DD-HH-MM-SS.csv
            if not re.match(r'^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}\.csv$', filename):
                # 非预期格式，跳过但推进游标
                last_pos = size_end
                continue

            # 有效项：大小>0，避免重复
            if size_val > 0:
                item = f"{filename}{size_val}"
                if item not in file_list:
                    file_list.append(item)

            # 继续从大小字段后解析
            last_pos = size_end

        # 只保留未处理的部分
        remain_bytes = buffer[last_pos:]
        buffer.clear()
        buffer.extend(remain_bytes)

       
        
    def _process_file_data(self, data: bytes):
        """处理文件下载数据"""
        self.recv_buffer.extend(data)
        
        while True:
            frame = self._extract_frame()
            if frame is None:
                break
            self._process_frame(frame)
    
    def _extract_frame(self) -> Optional[bytes]:
        """提取数据帧"""
        buf = self.recv_buffer
        min_len = Config.HEADER_LEN + 1 + 2 + 2 + 2  # 帧头+类型+序号+长度+帧尾
        
        if len(buf) < min_len:
            return None
            
        # 查找帧头
        idx = buf.find(Config.FRAME_HEADER)
        if idx < 0:
            del buf[0]
            return None
            
        if idx > 0:
            del buf[:idx]
            
        if len(buf) < min_len:
            return None
            
        frame_type = buf[Config.HEADER_LEN]
        seq = struct.unpack('<H', buf[Config.HEADER_LEN+1:Config.HEADER_LEN+3])[0]
        data_len = struct.unpack('<H', buf[Config.HEADER_LEN+3:Config.HEADER_LEN+5])[0]
        
        full_len = Config.HEADER_LEN + 1 + 2 + 2 + data_len + 2
        if len(buf) < full_len:
            return None
            
        frame_data = buf[:full_len]
        del buf[:full_len]
        
        return frame_data
    
    def _process_frame(self, frame_bytes: bytes):
        """处理数据帧"""
        frame_type = frame_bytes[2]
        seq = struct.unpack('<H', frame_bytes[3:5])[0]
        data_len = struct.unpack('<H', frame_bytes[5:7])[0]
        data = frame_bytes[7:7+data_len]
        
        if frame_type == Config.START_FRAME:
            # 开始帧
            if data_len >= 4:
                self.expected_lines = struct.unpack('<I', data[:4])[0]
            else:
                self.expected_lines = 0
                
            self.received_lines = 0
            self.received_seq_set.clear()
            self.content_list.clear()
            
            print(f"[开始下载] 文件: {self.current_file}, 预期行数: {self.expected_lines}")
            
        elif frame_type == Config.DATA_FRAME:
            # 数据帧
            if seq in self.received_seq_set:
                return
                
            self.received_seq_set.add(seq)
            self.received_lines += 1
            
            try:
                content = data.decode('utf-8').strip()
                self.content_list.append(content)
            except:
                content = ''
                
            # 发送下载进度事件
            progress = int(self.received_lines / max(self.expected_lines, 1) * 100)
            event_bus.emit(EventType.DOWNLOAD_PROGRESS, {
                'file': self.current_file,
                'progress': progress,
                'received': self.received_lines,
                'expected': self.expected_lines
            })
            
        elif frame_type == Config.END_FRAME:
            # 结束帧
            self._save_file()
            event_bus.emit(EventType.DOWNLOAD_COMPLETE, {
                'file': self.current_file,
                'success': True,
                'received': self.received_lines,
                'expected': self.expected_lines
            })
            
            self.current_file = None
            self.is_downloading = False
    
    def _save_file(self):
        """保存下载的文件"""
        if not self.current_file or not self.content_list:
            return
            
        try:
            file_path = os.path.join(self.download_path, self.current_file)
            with open(file_path, 'w', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                for content in self.content_list:
                    row = content.split(',')
                    writer.writerow(row)
                    
            print(f"[文件保存成功] {file_path}, 行数: {len(self.content_list)}")
            
        except Exception as e:
            print(f"文件保存失败: {e}")
    
    def get_files_list(self) -> List[str]:
        """获取文件列表"""
        return self.files_list.copy()
    
    def stop_download(self):
        """停止下载"""
        self.is_downloading = False
        self.is_listing = False
        self.current_file = None
        self.content_list.clear()
        self.recv_buffer.clear()
        self.file_buffer.clear()